package exe;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.StringReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * This class is to convert a dynamic PHP file to an HTML URI string, so the
 * JHAVE psuedocode panel does not have to query the server twice in order to
 * get the data. We can do it all in one pass with this adapter.
 * 
 * @author hietpb97 - Brad Hietpas
 * @version %I%, %G%
 */
public class PhpAdapter {

	/**
	 * This method grabs the php file's source code portion and converts it
	 * directly to html and highlights the given line number from the start
	 * position to the end position.
	 * 
	 * @param phpLocation
	 *            -Location of original PHP file
	 * @param lineNumber
	 *            -Line to highlight
	 * @param start
	 *            -Starting character to highlight
	 * @param end
	 *            -Ending character to highlight
	 * @return string -a string representation of a URI
	 */
	public static String make_uri(String phpLocation, int lineNumber,
			int start, int end) {
		// Get source from PHP file
		String source = getSourceFromPHP(phpLocation);

		// Modify the source, (change color of linenumber with html formatting)
		String modifiedSource = editSource(source, lineNumber, start, end);

		// Wrap with html
		StringBuilder content = new StringBuilder();
		content.append("<html><head><title>Program</title>"
				+ "</head><body><pre>\r\n");
		content.append(modifiedSource);
		content.append("</pre></body></html>");
		System.out.println(content.toString());

		// Make into uri
		URI uri = null;
		try {
			uri = new URI("str", content.toString(), "");
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}

		// Return URI
		return uri.toASCIIString();
	}

	/**
	 * This method grabs the php file's source code portion and converts it
	 * directly to html and highlights the given line number.
	 * 
	 * @param phpLocation
	 *            -Location of original PHP file
	 * @param lineNumber
	 *            -Line to highlight
	 * @return string -a string representation of a URI
	 */
	public static String make_uri(String phpLocation, int lineNumber) {
		return make_uri(phpLocation, lineNumber, -1, -1);
	}
	
	/**
	 * Converts a php file url and parameters directly to html right away so 
	 * there does client does not need to make another call to the server.
	 * 
	 * @param phpURL   -php url in the format of xxx.php?file=xxx.php&line=x
	 * @return ascii html stuff.
	 */
	public static String makeHtmlUriFromPhpUrl(String phpURL){
		//mm.php?file=
		String phpLocation = phpURL;
		int line = -1;
		int start = -1;
		int end = -1;
		
		Pattern patLine = Pattern.compile("&(amp;)?line=[0-9]+");
		Pattern patStart= Pattern.compile("&(amp;)?start=[0-9]+");
		Pattern patEnd = Pattern.compile("&(amp;)?end=[0-9]+");
		Matcher matLine = patLine.matcher(phpURL);
		Matcher matStart = patStart.matcher(phpURL);
		Matcher matEnd = patEnd.matcher(phpURL);
		
		
		if(matLine.find()){
			int index = matLine.group().indexOf("=");
			line = Integer.parseInt(matLine.group().substring(index+1));
		}
		if(matStart.find()){
			int index = matStart.group().indexOf("=");
			start = Integer.parseInt(matStart.group().substring(index+1));
		}
		if(matEnd.find()){
			int index = matEnd.group().indexOf("=");
			end = Integer.parseInt(matEnd.group().substring(index+1));
		}
		
		if(phpURL.contains(".php?file=")){
			int index = phpURL.indexOf(".php?file=");
			String phpFile = phpURL.substring(index+10);
			int endIndex = phpFile.indexOf(".php")+4;   //TODO not sure if it ends with .php
			phpLocation = phpFile.substring(0, endIndex);
		}
		
		if(!phpLocation.equals(phpURL) && line!=-1){
			return make_uri(phpLocation, line, start, end);
		}else{
			return phpURL;
		}
	}

	private static String getSourceFromPHP(String phpLocation) {
		Scanner scan;
		StringBuilder sb = new StringBuilder();

		try {
			scan = new Scanner(new File(phpLocation));

			while (scan.hasNextLine()) {
				sb.append(scan.nextLine());
			}

			String php = sb.toString();
			int startOfParse = php.indexOf("$pgm = array(");
			php = php.substring(startOfParse + 13);

			StringReader sr = new StringReader(php);

			char c;
			boolean inQuotes = false;
			boolean escapeChar = false;
			sb = new StringBuilder();
			while ((c = (char) sr.read()) != -1) {
				if (!inQuotes) {
					if (c == ')') {
						break;
					} else if (c == '\"') {
						inQuotes = true;
					}
				} else {
					if (c == '\\') {
						escapeChar = true;
					} else if (c == '\"' && escapeChar == true) {
						sb.delete(sb.length() - 1, sb.length());
					} else if (c == '\"' && escapeChar == false) {
						sb.append("\r\n");
						inQuotes = false;
						continue;
					} else {
						escapeChar = false;
					}
					sb.append(c);
				}
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		return sb.toString();
	}

	private static String editSource(String source, int lineNumber, int start,
			int end) {
		StringBuilder sb = new StringBuilder();
		Scanner scan = new Scanner(source);
		int lineNum = lineNumber;
		int i = 0;
		while (scan.hasNext()) {
			String line = scan.nextLine();

			if (lineNum == i++) {
				// If only part of line needs to be highlighted
				if (start != -1 && end != -1) {
					sb.append(line.substring(0, start));
					sb.append("<span style=\"color: ");
					sb.append("#FF0000;");
					sb.append("\">");
					sb.append(line.substring(start, end));
					sb.append("</span>");
					sb.append(line.substring(end, line.length()));
					sb.append("<br />");
				} else { // Whole line needs to be highlighted
					sb.append("<span style=\"color: ");
					sb.append("#FF0000;");
					sb.append("\">");
					sb.append(line);
					sb.append("</span>");
					sb.append("<br />");
				}
			} else {
				sb.append(line);
				sb.append("<br />");
			}
		}

		String colorizedSource = sb.toString();

		String htmlizedSource = fixHTMLSourceEscapeCharacters(colorizedSource);
		return htmlizedSource;
	}

	private static String fixHTMLSourceEscapeCharacters(String source) {

		// Building HTML Escape Characters
		HashMap<String, Boolean> escapeMap = new HashMap<String, Boolean>(997);
		String[] escapeList = { "&euro;", "&nbsp;", "&quot;", "&amp;", "&lt;",
				"&gt;", "&iexcl;", "&cent;", "&pound;", "&curren;", "&yen;",
				"&brvbar;", "&sect;", "&uml;", "&copy;", "&ordf;", "&not;",
				"&shy;", "&reg;", "&macr;", "&deg;", "&plusmn;", "&sup2;",
				"&sup3;", "&acute;", "&micro;", "&para;", "&middot;",
				"&cedil;", "&sup1;", "&ordm;", "&raquo;", "&frac14;",
				"&frac12;", "&frac34;", "&iquest;", "&Agrave;", "&Aacute;",
				"&Acirc;", "&Atilde;", "&Auml;", "&Aring;", "&AElig;",
				"&Ccedil;", "&Egrave;", "&Eacute;", "&Ecirc;", "&Euml;",
				"&Igrave;", "&Iacute;", "&Icirc;", "&Iuml;", "&ETH;",
				"&Ntilde;", "&Ograve;", "&Oacute;", "&Ocirc;", "&Otilde;",
				"&Ouml;", "&times;", "&Oslash;", "&Ugrave;", "&Uacute;",
				"&Ucirc;", "&Uuml;", "&Yacute;", "&THORN;", "&szlig;",
				"&agrave;", "&aacute;", "&acirc;", "&atilde;", "&auml;",
				"&aring;", "&aelig;", "&ccedil;", "&egrave;", "&eacute;",
				"&ecirc;", "&euml;", "&igrave;", "&iacute;", "&icirc;",
				"&iuml;", "&eth;", "&ntilde;", "&ograve;", "&oacute;",
				"&ocirc;", "&otilde;", "&ouml;", "&divide;", "&oslash;",
				"&ugrave;", "&uacute;", "&ucirc;", "&uuml;", "&yacute;",
				"&thorn;" };
		for (int i = 32; i <= 383; i++) {
			escapeMap.put("&#" + i + ";", true);
		}
		for (String s : escapeList) {
			escapeMap.put(s, true);
		}

		StringBuilder sb = new StringBuilder(); // for new html-ized source code

		boolean isEscape = false;
		for (int i = 0; i < source.length(); i++) {
			if (source.charAt(i) == '&') {
				for (int j = 2; j < 10 && j + i < source.length(); j++) {
					if (escapeMap.get(source.substring(i, i + j)) != null) {
						isEscape = true;
						break;
					}
				}
				if (!isEscape) {
					sb.append("&amp;"); // Convert the & in source code to &amp;
										// to be displayed correctly in HTML
										// pseudocode viewport.
				} else {
					sb.append(source.charAt(i)); // Otherwise its meant to be an
													// HTML escaped character
													// and not an & in source
													// code.
				}
				isEscape = false;
			} else {
				sb.append(source.charAt(i));
			}
		}

		return sb.toString();
	}
}
